#!/usr/bin/perl

use lib::abs qw( base_lib lib );
use JIP::ToolSet; # load my commonly-used modules in a single import

use Getopt::Long qw( GetOptions );       # CPAN modules
use File::Spec::Functions qw( catfile ); #
use FindBin qw( $RealBin );              #
use Pod::Usage qw( pod2usage );          #
use Carp qw( croak );                    #
use DateTime;                            #

use Configurator;                # base lib
use Log;                         #
use Proc::Lock;                  #
use JIP::Utils qw( getLogName ); #

use DB::Schema;                          # searcher custom modules
use Net::FastScan;                       #
use Net::SMB::NameResolver qw( getNBN ); #

#-- ui -------------------------------------------------------------------------
GetOptions( 'help' => \my $isHelp, 'mode=s' => \my $scanMode );

pod2usage( -exitstatus => 0, -output => \*STDOUT, -verbose => 2 ) if $isHelp;

pod2usage(
    -exitstatus => 1,
    -output     => \*STDERR,
    -message    => 'required parameter "mode" not defined'
) unless $scanMode;

#-- init settings --------------------------------------------------------------
my $conf = Configurator->new( conf => 'robo_conf.pm' )->getSettings;

#-- init log -------------------------------------------------------------------
our $log = Log->new(
    log_file  => catfile( $conf->fast_scanner->log_dir, getLogName() ),
    log_level => $conf->fast_scanner->log_level,
);

#-- signal handlers ------------------------------------------------------------
require( catfile( $RealBin, 'signal_handlers.pl' ) );

#-- setup lock -----------------------------------------------------------------
$log->Note('setup lock');

my $process = Proc::Lock->new( lock_file => $conf->fast_scanner->lock_file );

$process->lock;

#-- orm handle -----------------------------------------------------------------
my $orm = DB::Schema->connect(
    $conf->sql->dsn,
    $conf->sql->user,
    $conf->sql->pass,
);

$orm->storage->debug( $conf->sql->isDebug );

#-- main -----------------------------------------------------------------------
my @knownHosts;

if ( $scanMode eq 'normal' ) {
    @knownHosts = $orm->resultset('Ftphosts')->get_column('hostip')->all;
}
elsif ( $scanMode eq 'express' ) {
    @knownHosts = $orm->resultset('Ftphosts')->search(
        {
            lastfastscan => {
                '>', DateTime->now( time_zone => $conf->main->time_zone )
                             ->subtract( hours => $conf->main->ageing )
                             ->strftime('%F %T')
            }
        },
        {
            columns => [ 'hostip' ],
        },
    )->get_column('hostip')->all;
}
else {
    croak 'unexpected value in "mode"';
}

$log->Note('fire started scan loop');

my $scanner = Net::FastScan->new(
    max_threads => $conf->fast_scanner->max_threads
);

foreach my $eachIPAddress ( $scanner->getActiveFTPServers( @knownHosts ) ) {
    my $NETBIOSName = getNBN( ip_address => $eachIPAddress ) // '-';

    $log->Note( "host '". $eachIPAddress ."' with nbn '". $NETBIOSName ."'" );

    my $currentDateTime = DateTime->now( time_zone => $conf->main->time_zone )
                                  ->strftime('%F %T');

    $orm->resultset('Ftphosts')
        ->search( { hostip => $eachIPAddress, } )
        ->update( { nbn => $NETBIOSName, lastfastscan => $currentDateTime, } );
}

#-- finalyze -------------------------------------------------------------------
$log->Note('-- finalyze -----------------------------------------------------');

$log->Note('unset lock...') and $process->unlock;

$log->Note('exiting');

#-- end ------------------------------------------------------------------------

__END__

=head1 NAME

fast_scanner.pl - fast multithreaded FTP scanner

=head1 SYNOPSIS

fast_scanner.pl [options]

Options:

--help            - full documentation

--mode            - scan mode (normal or express)

=cut
